<!DOCTYPE html> 
<html>
<head>
<title>Skyline75489</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" href="../static/styles/github.css">
<link rel="stylesheet" href="../static/post.css">
</head>
<body>

<div class="wrapper">
<div class="header">
	<span class="blog-name">Skyline75489</span>

<a class="nav" href="../index.html">Home</a>
<a class="nav" href="about.html">About</a>
</div>
<div class="content">

<h1>Python WSGI学习笔记</h1>
<p>WSGI的具体内容就不在这里赘述了，官方的<a href="http://legacy.python.org/dev/peps/pep-0333/">PEP 333</a>和<a href="http://legacy.python.org/dev/peps/pep-3333/">PEP 3333</a>说的很详细，不想看英文文档的可以看看<a href="https://jasonlvhit.github.io/articles/wsgi/">这篇文章</a>，我感觉写的挺简单易懂的。简单来说，WSGI规范把Web组件分成了三个部分，从上到下分别是Application/Framework，Middleware，和Server/Gateway，其中Middleware并不是必需的。有了这三个部分，就可以构建一个完整的Web应用。我主要从下面这几个方面说一下我自己的体会。</p>
<h3>组件之间的高度解耦</h3>
<p>在阅读WSGI规范的时候，让我感觉比较给力的地方，是这三个层次几乎实现了完美的解耦：Application/Framework层只需要实现一个callable就可以了；Server/Gateway层，只需要把客户端的请求处理成environ，然后传给Application/Framework就可以了；Middleware这个基本上相当于是一个装饰器，同样也是高内聚低耦合的。这三个部分互相之间几乎没有依赖，这使得使用Python开发和部署Web应用变得十分灵活。</p>
<p>为了解耦的需要，WSGI把对Application的接口减少到只有一个变量(environ)，一个callable(start_response) 。这样做为解耦带来方便的同时，也为直接使用WSGI开发带来了困难。要在一个Application里面处理URL Routing，Cookie，Caching之类的，可想而知需要使用多少if else才能完成，哪怕使用Middleware恐怕也不会简单到哪里去。因此我们还需要使用Web开发框架。</p>
<h3>与Web框架之间的异同</h3>
<p>WSGI的<a href="http://legacy.python.org/dev/peps/pep-3333/#questions-and-answers">Q&amp;A</a>里明确说明了，WSGI并不是一个Web开发框架，而是一个Web框架和服务器之间交互的接口规范( It's just a way for frameworks to talk to web servers, and vice versa.)。因此不应该把它当成一个框架来使用（虽然实际上是可以这么搞的，如果你不嫌麻烦的话，参看下面）。</p>
<p>Web框架是对Low Level的WSGI进行封装之后的产物，通常Web框架对开发者提供了更为友好的接口，例如使用Flask框架，可以使用如下的代码来构建一个输出Hello World的小应用:</p>
<pre><code class="lang-python">from flask import Flask
app = Flask(__name__)

@app.route(’/’)
def hello_world():
    return ’Hello World!’

if __name__ == ’__main__’:
    app.run()
</code></pre>
<p>而如果直接使用WSGI，可能需要写成这样:</p>
<pre><code class="lang-python">from wsgiref.simple_server import make_server

HELLO_WORLD = b&quot;Hello World!\n&quot;

def my_app(environ, start_response):
    status = &#39;200 OK&#39;
    response_headers = [(&#39;Content-type&#39;, &#39;text/plain&#39;)]
    start_response(status, response_headers)
    path = environ[&#39;PATH_INFO&#39;]
    if path == &#39;&#39; or path == &#39;/&#39;:
        return [HELLO_WORLD]
    else:
        raise NotFound

server = make_server(&#39;127.0.0.1&#39;, 5000, my_app)
server.serve_forever()
</code></pre>
<p>上面两种代码的运行结果是一样的，很明显使用框架的代码更加简单和明了，可扩展性也更强。</p>
<p>同时，大部分Web框架都提供了符合WSGI规范的接口，也就是说可以作为WSGI app来使用：</p>
<pre><code class="lang-python"># Flask
from flask import Flask
app = Flask(__name__)

my_app = app.wsgi_app
</code></pre>
<pre><code class="lang-python"># Tornado 
import tornado.web
import tornado.wsgi

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(&quot;Hello, world&quot;)

application = tornado.wsgi.WSGIApplication([
        (r&quot;/&quot;, MainHandler),
])
</code></pre>
<pre><code class="lang-python"># Django
from django.core.wsgi import get_wsgi_application

application = get_wsgi_application()
</code></pre>
<p>因此使用框架开发可以兼具灵活性和与不同Server的兼容性。</p>
<h3>多样的部署选择</h3>
<p>由于上面提到的高度解耦特性，理论上，任何一个符合WSGI规范的App都可以部署在任何一个实现了WSGI规范的Server上，这给Python Web应用的部署带来了极大的灵活性。</p>
<p>Flask自带了一个基于Werkzeug的调试用服务器。根据Flask的文档，在生产环境不应该使用内建的调试服务器，而应该采取以下方式之一进行部署：</p>
<ul>
<li>mod_wsgi(Apache)</li>
<li>独立的WSGI Container（其中包括Gunicorn, Tornado HTTPServer, Twisted Web等）</li>
<li>uWSGI</li>
<li>FastCGI</li>
<li>CGI</li>
</ul>
<p>实际看来，直接使用mod_wsgi，FastCGI，CGI这些的比较少，这些可能更适合部署PHP之类的应用。用的比较多的是，使用Apache或Nginx做反向代理服务器，然后使用uWSGI或者那几个WSGI Container中的一个来做后端的Server。</p>


</div>
</div>
<script src="../static/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>

</html>
